package jmh;

import com.evoupsight.monitorpass.perfs.ExprEvalutorHelper;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyShell;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import utils.GroovyShellUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 基准测试spel表达式和groovy表达式
 *
 * @author evoup
 */ /*
http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Fork(value = 2, jvmArgs = {"-Xms4G", "-Xmx4G"})
// 默认Warmup和Measurement都是5
// @Warmup(iterations = 3)
// @Measurement(iterations = 8)
public class BenchmarkExpression {

    @Param({"10000000"})
    private int N;

    private List<String> DATA_FOR_TESTING;
    private List<Boolean> DATA_FOR_TESTING2;


    public static void main(String[] args) throws RunnerException {

        Options opt = new OptionsBuilder()
                .include(BenchmarkExpression.class.getSimpleName())
                .forks(1)
                .build();

        new Runner(opt).run();
    }

    @Setup
    public void setup() {
        DATA_FOR_TESTING = createData();
        DATA_FOR_TESTING2 = createData2();
    }

    /**
     * spring spel表达式
     */
    @Benchmark
    public void spel(Blackhole bh) {
        int i = 0;
        while (i < DATA_FOR_TESTING.size()) {
            String exp = DATA_FOR_TESTING.get(i);
            ExpressionParser expressionParser = new SpelExpressionParser();
            Expression expression = expressionParser.parseExpression(exp);
            Boolean result = (Boolean) expression.getValue();
            bh.consume(result);
            i++;
        }
    }

    /**
     * 没有优化过的
     */
    //@Benchmark
    public void groovyShell(Blackhole bh) {
        int i = 0;
        GroovyShell shell = new GroovyShell();
        while (i < DATA_FOR_TESTING.size()) {
            String exp = DATA_FOR_TESTING.get(i);
            Object evaluate = shell.evaluate(exp);
            shell.getClassLoader().clearCache();
            bh.consume(evaluate);
            i++;
        }
    }

    /**
     * 没有优化过的
     */
    //@Benchmark
    public void groovy(Blackhole bh) {
        int i = 0;
        while (i < DATA_FOR_TESTING.size()) {
            synchronized (this) {
                String exp = DATA_FOR_TESTING.get(i);
                GroovyClassLoader loader = new GroovyClassLoader();
                Object run = InvokerHelper.createScript(loader.parseClass(exp), new Binding()).run();
                loader.clearCache();
                bh.consume(run);
            }
            i++;
        }
    }

    /**
     * 优化过的
     */
    @Benchmark
    public void groovyPlus(Blackhole bh) {
        int i = 0;
        long randomSeed = 10001L;
        Random random = new Random(randomSeed);
        random.setSeed(randomSeed);
        while (i < DATA_FOR_TESTING.size()) {
            Map<String, Object> params = new TreeMap<>();
            String s = DATA_FOR_TESTING.get(i);
            params.put("t1", s);
            params.put("t2", s);
            params.put("t3", s);
            params.put("t4", s);
            params.put("t5", s);
            params.put("t6", s);
            String boolExpr = "t1 && t2 || t3 || t4 && (t5 || t6)";
            GroovyShellUtils.execExpr(boolExpr, params, Boolean.class);
            i++;
        }
    }

    @Benchmark
    public void antlr4(Blackhole bh) {
        int i = 0;
        String boolExpr = "1 && 2 || 3 || 4 && (5 || 6)";
        while (i < DATA_FOR_TESTING2.size()) {
            Boolean b = DATA_FOR_TESTING2.get(i);
            Map<String, Boolean> params = new TreeMap<>();
            params.put("1", b);
            params.put("2", b);
            params.put("3", b);
            params.put("4", b);
            params.put("5", b);
            params.put("6", b);
            ExprEvalutorHelper.exec(boolExpr, params);
            i++;
        }
    }


    private List<String> createData() {
        long randomSeed = 10001L;
        List<String> data = new ArrayList<>();
        Random random = new Random(randomSeed);
        random.setSeed(randomSeed);
        for (int i = 0; i < N; i++) {
            data.add(String.format("%.2f", random.nextDouble()) + ">" + String.format("%.2f", random.nextDouble()));
        }
        return data;
    }

    private List<Boolean> createData2() {
        long randomSeed = 10001L;
        List<Boolean> data = new ArrayList<>();
        Random random = new Random(randomSeed);
        random.setSeed(randomSeed);
        for (int i = 0; i < N; i++) {
            data.add(random.nextBoolean());
        }
        return data;
    }


}
